package com.github.data_manage.controller

import cn.dev33.satoken.stp.StpUtil
import cn.hutool.core.bean.BeanUtil
import cn.hutool.core.bean.copier.CopyOptions
import com.fasterxml.jackson.core.type.TypeReference
import com.fasterxml.jackson.databind.ObjectMapper
import com.github.data_manage.common.CommonResult
import com.github.data_manage.config.BeanConfig
import com.github.data_manage.config.FUND_TEMP_DIR
import com.github.data_manage.config.SystemException
import com.github.data_manage.dao.CompanyDao
import com.github.data_manage.dao.FundDao
import com.github.data_manage.dao.FundFileUploadRecordDao
import com.github.data_manage.dao.FundMeetingDao
import com.github.data_manage.dao.UserMessageDao
import com.github.data_manage.domain.po.Company
import com.github.data_manage.domain.po.Fund
import com.github.data_manage.domain.po.FundMeeting
import com.github.data_manage.domain.po.FundPerson
import com.github.data_manage.domain.po.FundTranche
import com.github.data_manage.domain.po.UserMessage
import com.github.data_manage.domain.vo.FundPersonVo
import com.github.data_manage.domain.vo.FundSaveOrUpdateVo
import com.github.data_manage.enum.FundFileType
import com.github.data_manage.enum.FundMeetingType
import com.github.data_manage.util.AuthContextHolder
import io.swagger.v3.oas.annotations.tags.Tag
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.joinAll
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import org.slf4j.LoggerFactory
import org.springframework.util.StringUtils
import org.springframework.web.bind.annotation.*
import java.io.File
import java.math.BigDecimal
import java.time.LocalDateTime
import java.util.concurrent.CompletableFuture


@RestController
@Tag(name = "AiResponseController", description = "ai数据接收管理")
@RequestMapping("/ai/result")
class AiResponseController(
    private val objectMapper: ObjectMapper,
    private val fundFileUploadRecordDao: FundFileUploadRecordDao,
    private val userMessageDao: UserMessageDao,
    private val fundDao: FundDao,
    private val companyDao: CompanyDao,
    private val fundController: FundController,
    private val fundFileController: FundFileController,
    private val fundMeetingController: FundMeetingController,
    private val fundMeetingDao: FundMeetingDao,
) {

    companion object {

        private val logger = LoggerFactory.getLogger(AiResponseController::class.java)

    }


    @PostMapping("/fundInfo")
    fun aiFundResultUpload(
        @RequestBody result: String,
    ): CommonResult<Boolean> {
        logger.info("ai file result {}", result)
        if (!result.contains("llm_response")) {
            logger.warn("上报数据不正确")
            return CommonResult.success(true)
        }

        val tree = objectMapper.readTree(result)

        val jobId = tree["job_id"].textValue()

        val fileUploadRecordMap = fundFileUploadRecordDao.findAllByJobId(jobId).groupBy { it.fileName }
        val fileNames = tree["files_process"].map { it.textValue() }

        val llmResponseDataNode = tree["llm_response"]

        val fundInfoString = llmResponseDataNode["fund"].toPrettyString()
        logger.info("fundInfoString: [{}]", fundInfoString)
        val fundInfo = objectMapper.readValue(fundInfoString, Fund::class.java)
        if (!StringUtils.hasText(fundInfo.fundName)) {
            logger.error("file [{}] 未找到基金名称", fileNames)
            Thread.startVirtualThread {
                for (fileName in fileNames) {
                    val fileUploadRecord = fileUploadRecordMap[fileName]?.first() ?: continue
                    try {
                        val userMessage = UserMessage()
                        userMessage.userId = fileUploadRecord.userId!!
                        userMessage.fundId = null
                        userMessage.message =
                            "${FundFileType.valueOf(fileUploadRecord.fileType).message}文件[${fileName}]解析完成,未找到对应的基金数据"
                        userMessage.alreadyRead = false
                        userMessage.fileUploadRecordId = fileUploadRecord.id!!
                        userMessage.createTime = LocalDateTime.now()
                        userMessageDao.save(userMessage)
                    } catch (e: Exception) {
                        logger.error("保存用户消息出错", e)
                    }
                    fundFileUploadRecordDao.delete(fileUploadRecord)

                }

            }
            return CommonResult.success(false)
        }
        val dbFund = fundDao.findByFundName(fundInfo.fundName!!) ?: Fund()
        BeanUtil.copyProperties(
            fundInfo, dbFund, CopyOptions.create().ignoreNullValue()
                .setIgnoreProperties("fundId")
        )
        logger.info("file [{}] 基金信息为 [{}]", fileNames, BeanConfig.objectMapper.writeValueAsString(dbFund))

        val fundTranchesInfoString = llmResponseDataNode["fundTranches"].toPrettyString()
        val fundTranches =
            objectMapper.readValue(fundTranchesInfoString, object : TypeReference<List<FundTranche>>() {})
                .filter { StringUtils.hasText(it.name) }
        logger.info(
            "file [{}] 基金 [{}] 份额信息信息为 {}",
            fileNames,
            dbFund.fundName,
            BeanConfig.objectMapper.writeValueAsString(fundTranches)
        )

        val companyInfoString = llmResponseDataNode["companyInfo"].toPrettyString()
        val company =
            objectMapper.readValue(companyInfoString, object : TypeReference<Company>() {})
        val dbCompany = if (!StringUtils.hasText(company.companyName)) {
            logger.error("file [{}] 未找到公司名称", fileNames)
            null
        } else {
            val newCompany = companyDao.findCompanyByCompanyNameEquals(company.companyName!!) ?: Company()
            BeanUtil.copyProperties(
                company,
                newCompany,
                CopyOptions.create().ignoreNullValue()
                    .setIgnoreProperties("companyId")
            )
            newCompany
        }

        val fundSaveOrUpdateVo = FundSaveOrUpdateVo()
        fundSaveOrUpdateVo.fund = dbFund
        fundSaveOrUpdateVo.fundTranches = fundTranches
        fundSaveOrUpdateVo.companyInfo = dbCompany
        try {
            fundController.saveOrUpdateFundWithTranche(fundSaveOrUpdateVo, false)
            logger.info("保存基金信息成功")
        } catch (e: Exception) {
            logger.error("保存基金信息出错", e)
            Thread.startVirtualThread {
                for (fileName in fileNames) {
                    val fileUploadRecord = fileUploadRecordMap[fileName]?.first() ?: continue
                    val userMessage = UserMessage()
                    userMessage.userId = fileUploadRecord.userId!!
                    userMessage.fundId = null
                    userMessage.message =
                        "${FundFileType.valueOf(fileUploadRecord.fileType).message}文件[${fileName}]解析结果保存失败"
                    userMessage.alreadyRead = false
                    userMessage.fileUploadRecordId = fileUploadRecord.id!!
                    userMessage.createTime = LocalDateTime.now()
                    userMessageDao.save(userMessage)
                }
            }
            return CommonResult.success(false)
        }
        Thread.startVirtualThread {
            val fund = fundDao.findByFundName(dbFund.fundName!!) ?: return@startVirtualThread
            val fundPersons = llmResponseDataNode["memberInfo"]?.map { memberInfoNode ->
                val name = memberInfoNode["name"].textValue()
                val position = memberInfoNode["position"].textValue()
                val phone = memberInfoNode["phone"].textValue()
                val note = memberInfoNode["note"].textValue()
                val linkedUrl = memberInfoNode["linkedUrl"].textValue()
                if (!StringUtils.hasText(name)) {
                    return@map null
                }

                val fundPerson = FundPerson()
                fundPerson.name = name
                fundPerson.position = position
                fundPerson.linkedin = linkedUrl
                fundPerson
            }?.filterNotNull() ?: return@startVirtualThread
            fundController.saveOrUpdateFundPersons(
                fund.fundId!!,
                FundPersonVo(
                    fundPersons,
                    emptyList()
                )
            )
        }


        Thread.startVirtualThread {
            for (fileName in fileNames) {
                val fileUploadRecord = fileUploadRecordMap[fileName]?.first() ?: continue
                try {
                    val fund = fundDao.findByFundName(dbFund.fundName!!) ?: return@startVirtualThread
                    fundFileController.saveFile(
                        File("$FUND_TEMP_DIR/${fileName}"),
                        fund.fundId!!,
                        FundFileType.valueOf(fileUploadRecord.fileType),
                    )
                    logger.info("保存基金文件成功")
                } catch (e: Exception) {
                    logger.error("保存基金文件出错", e)
                }
                try {
                    val userMessage = UserMessage()
                    userMessage.userId = fileUploadRecord.userId!!
                    userMessage.fundId = dbFund.fundId!!
                    userMessage.message =
                        "${FundFileType.valueOf(fileUploadRecord.fileType).message}文件[${fileName}]解析完成"
                    userMessage.alreadyRead = false
                    userMessage.fileUploadRecordId = fileUploadRecord.id!!
                    userMessage.createTime = LocalDateTime.now()
                    userMessageDao.save(userMessage)
                    logger.info("保存用户消息成功")
                } catch (e: Exception) {
                    logger.error("保存用户消息出错", e)
                }
                fundFileUploadRecordDao.delete(fileUploadRecord)

            }
        }
        return CommonResult.success(true)
    }


    @PostMapping("/beforeMeeting")
    fun aiBeforeMeeting(
        @RequestBody result: String,
    ): CommonResult<Boolean> {
        logger.info(" aiBeforeMeeting result {}", result)
        val tree = objectMapper.readTree(result)
        val jobId = tree["job_id"].textValue()
        val fileUploadRecords = fundFileUploadRecordDao.findAllByJobId(jobId)

        val fundId = fileUploadRecords.mapNotNull { it.fundId }.distinct().first()
        val fund = fundDao.findById(fundId).orElseThrow { SystemException("基金信息不正确") }
        val userId = fileUploadRecords.mapNotNull { it.userId }.distinct().first()
        val statusCode = tree["status_code"].intValue()
        if (statusCode != 200) {
            val userMessage = UserMessage()
            userMessage.userId = userId
            userMessage.fundId = fundId
            userMessage.message =
                "基金${fund.fundName!!}会前纪要生成失败"
            userMessage.alreadyRead = false
            userMessage.createTime = LocalDateTime.now()
            userMessageDao.save(userMessage)
            return CommonResult.success(true)
        }
        val fundMeeting = FundMeeting()
        fundMeeting.fundId = fundId
        fundMeeting.persons = "AI"
        fundMeeting.insertBy = "AI"
        fundMeeting.location = "AI"
        fundMeeting.opponentPersons = "AI"
        fundMeeting.comments = "AI"
        fundMeeting.otter = "AI"
        fundMeeting.score = BigDecimal.ZERO
        fundMeeting.createTime = LocalDateTime.now()
        fundMeeting.meetingTime = LocalDateTime.now()
        fundMeeting.meetingType = FundMeetingType.AI_BEFORE_MEETING.name
        fundMeeting.meetingDetails = tree["html_content"].textValue()
        fundMeeting.aiMeetingDetails = tree["html_content"].textValue()

        try {
            fundMeetingController.addFundMeeting(fundMeeting)

            val userMessage = UserMessage()
            userMessage.userId = userId
            userMessage.fundId = fundId
            userMessage.message =
                "${fund.fundName!!}会前纪要生成成功"
            userMessage.alreadyRead = false
            userMessage.createTime = LocalDateTime.now()
            userMessageDao.save(userMessage)
        } catch (e: Exception) {
            logger.error("生成基金[{}]的 ai 会前纪要失败", fundId, e)
        }

        return CommonResult.success(true)
    }

    @PostMapping("/afterMeeting")
    fun aiAfterMeeting(
        @RequestBody result: String,
    ): CommonResult<Boolean> {
        logger.info(" aiAfterMeeting result {}", result)
        val tree = objectMapper.readTree(result)
        val jobId = tree["job_id"].textValue()
        val statusCode = tree["status_code"].intValue()
        val fileUploadRecords = fundFileUploadRecordDao.findAllByJobId(jobId)
        val fundId = fileUploadRecords.mapNotNull { it.fundId }.distinct().first()
        val fund = fundDao.findById(fundId).orElseThrow { SystemException("基金信息不正确") }
        val userId = fileUploadRecords.mapNotNull { it.userId }.distinct().first()
        val fundMeetingId = fileUploadRecords.mapNotNull { it.fundMeetingId }.distinct().first()
        val fundMeeting = fundMeetingDao.findById(fundMeetingId).orElseThrow { SystemException("会议id不正确") }
        if (statusCode != 200) {
            val userMessage = UserMessage()
            userMessage.userId = userId
            userMessage.fundId = fundId
            userMessage.message =
                "基金${fund.fundName!!}会后纪要生成失败"
            userMessage.alreadyRead = false
            userMessage.createTime = LocalDateTime.now()
            userMessageDao.save(userMessage)
            return CommonResult.success(true)
        }
        fundMeeting.aiMeetingDetails = tree["html_content"].textValue()
        fundMeetingDao.save(fundMeeting)

        fundDao.findById(fundMeeting.fundId).orElseThrow { SystemException("指定的基金信息不存在") }
        fundMeetingController.sendEmail(fund,fundMeeting, FundMeetingType.AI_AFTER_MEETING)
        val userMessage = UserMessage()
        userMessage.userId = userId
        userMessage.fundId = fundId
        userMessage.message =
            "基金${fund.fundName!!}会后纪要生成成功"
        userMessage.alreadyRead = false
        userMessage.createTime = LocalDateTime.now()
        userMessageDao.save(userMessage)
        return CommonResult.success(true)
    }
}